/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
 *
 * @file    sample_public_venc.c
 * @brief   sample public venc module
 * @author  HiMobileCam NDK develop team
 * @date  2019-5-16
 */

#include "stdio.h"
#include "sample_public_venc.h"
#include "loadbmp.h"


#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* End of #ifdef __cplusplus */

HI_CHAR *SAMPLE_VENC_Pt2Str(HI_MAPI_PAYLOAD_TYPE_E enPayloadType)
{
    if (enPayloadType == HI_MAPI_PAYLOAD_TYPE_H264) {
        return "h264";
    } else if (enPayloadType == HI_MAPI_PAYLOAD_TYPE_H265) {
        return "h265";
    } else if (enPayloadType == HI_MAPI_PAYLOAD_TYPE_JPEG) {
        return "jpg";
    } else if (enPayloadType == HI_MAPI_PAYLOAD_TYPE_MJPEG) {
        return "mjp";
    } else {
        return "data";
    }
}

FILE* SAMPLE_AUDIO_OpenVideoFile(HI_CHAR* pFilePath)
{
    FILE* pFd;
    HI_CHAR aszFileName[FILE_FULL_PATH_MAX_LEN] = {0};
    HI_S32 s32Ret;

    if (pFilePath == HI_NULL) {
        printf("pFilePath is null!\n");
        return NULL;
    }

    s32Ret = snprintf_s(aszFileName, FILE_FULL_PATH_MAX_LEN, FILE_FULL_PATH_MAX_LEN, "%s", pFilePath);
    if (s32Ret == HI_FAILURE) {
        printf("%s snprintf_s failed :%#x\n", __FUNCTION__, s32Ret);
        return NULL;
    }

    pFd = fopen(aszFileName, "a+");
    if (pFd == HI_NULL) {
        pFd= fopen(aszFileName, "wb");
        if (pFd == HI_NULL) {
            printf("[%s][%d] fopen wb file: \"%s\" wb failed \n", __FUNCTION__, __LINE__, aszFileName);
            return NULL;
        }
    }

    printf("open file:\"%s\" for venc ok\n", aszFileName);
    return pFd;
}


HI_S32 SAMPLE_VENC_SaveJpegFile(HI_HANDLE VencHdl, HI_MAPI_VENC_DATA_S *pVStreamData,
    HI_VOID *pPrivateData)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_CHAR aFileName[FILE_FULL_PATH_MAX_LEN] = {0};
    FILE *pVencFile = NULL;
    static int u32count = 0;

    HI_CHAR *pDataPostfix = SAMPLE_VENC_Pt2Str(pVStreamData->astPack[0].stDataType.enPayloadType);

    if (pPrivateData != NULL) {
        s32Ret = snprintf_s(aFileName, FILE_FULL_PATH_MAX_LEN, FILE_FULL_PATH_MAX_LEN, "%s_chn%d_%d.%s",
            (char *)pPrivateData, VencHdl, u32count, pDataPostfix);
        if (s32Ret == HI_FAILURE) {
            printf("[%s][%d] snprintf_s error:%#x\n", __FUNCTION__, __LINE__, s32Ret);
            return HI_FAILURE;
        }

    } else {
        s32Ret = snprintf_s(aFileName, FILE_FULL_PATH_MAX_LEN, FILE_FULL_PATH_MAX_LEN, "%s_chn%d_%d.%s", "null",
            VencHdl, u32count, pDataPostfix);
        if (s32Ret == HI_FAILURE) {
            printf("[%s][%d] snprintf_s error:%#x\n", __FUNCTION__, __LINE__, s32Ret);
            return HI_FAILURE;
        }
    }

    pVencFile = fopen(aFileName, "w");

    if (pVencFile == NULL) {
        printf("fopen %s error.\n", aFileName);
        return -1;
    }

    HI_S32 i = 0;

    for (i = 0; i < pVStreamData->u32PackCount; i++) {
#if  defined (__DualSys__) || defined (__HuaweiLite__)
        HI_U64 u64PackPhyAddr = pVStreamData->astPack[i].au64PhyAddr[0];
        HI_U32 u32PackLen = pVStreamData->astPack[i].au32Len[0];
        HI_U32 u32PackOffset = pVStreamData->astPack[i].u32Offset;

        HI_U64 s_pDataVirtAddr = (HI_U64)(HI_UL)pVStreamData->astPack[i].apu8Addr[1];
        HI_U64 s_u64DataPhyAddr = pVStreamData->astPack[i].au64PhyAddr[1];
        HI_U32 s_u32DataLen = pVStreamData->astPack[i].au32Len[1];
        HI_VOID *pPackVirtAddr = pVStreamData->astPack[i].apu8Addr[0];

        if (u64PackPhyAddr + u32PackLen >= s_u64DataPhyAddr + s_u32DataLen) {
            /* physical address retrace in offset segment */
            if (u64PackPhyAddr + u32PackOffset >= s_u64DataPhyAddr + s_u32DataLen) {
                HI_VOID *pSrcVirtAddr = (HI_VOID *)(HI_UL)(s_pDataVirtAddr +
                    ((u64PackPhyAddr + u32PackOffset) - (s_u64DataPhyAddr + s_u32DataLen)));
                s32Ret = fwrite(pSrcVirtAddr, u32PackLen - u32PackOffset, 1, pVencFile);

                if (s32Ret < 0) {
                    printf("fwrite error %d\n", s32Ret);
                }
            } else {
                /* physical address retrace in data segment */
                HI_U32 u32Left = (s_u64DataPhyAddr + s_u32DataLen) - u64PackPhyAddr;
                s32Ret = fwrite((HI_VOID *)((HI_UL)pPackVirtAddr + u32PackOffset), u32Left - u32PackOffset, 1,
                    pVencFile);

                if (s32Ret < 0) {
                    printf("fwrite error %d\n", s32Ret);
                }

                s32Ret = fwrite((HI_VOID *)(HI_UL)s_pDataVirtAddr, u32PackLen - u32Left, 1, pVencFile);

                if (s32Ret < 0) {
                    printf("fwrite error %d\n", s32Ret);
                }
            }
        } else {
            /* physical address retrace does not happen */
            s32Ret = fwrite((HI_VOID *)((HI_UL)pPackVirtAddr + u32PackOffset), u32PackLen - u32PackOffset, 1,
                pVencFile);

            if (s32Ret < 0) {
                printf("fwrite error %d\n", s32Ret);
            }
        }

#else
        HI_U8* pPackVirtAddr = pVStreamData->astPack[i].apu8Addr[0];
        HI_U32 u32PackLen = pVStreamData->astPack[i].au32Len[0];
        HI_U32 u32PackOffset = pVStreamData->astPack[i].u32Offset;
        s32Ret = fwrite(pPackVirtAddr + u32PackOffset, u32PackLen - u32PackOffset, 1, pVencFile);
        if (s32Ret < 0) {
            printf("fwrite error %x\n", s32Ret);
        }
#endif

        if (fflush(pVencFile)) {
            perror("fflush file\n");
        }

    }

    if (fclose(pVencFile)) {
        printf("fclose error\n");
    }

    printf("save jpeg %d\n", u32count);
    u32count++;

    return HI_SUCCESS;
}

HI_S32 SAMPLE_VENC_SaveStreamFile(HI_HANDLE VencHdl, HI_MAPI_VENC_DATA_S *pVStreamData,
    HI_VOID *pPrivateData)
{
    HI_S32 s32Ret = HI_SUCCESS;
    FILE *pFd = (FILE *)pPrivateData;

    HI_S32 i = 0;

    for (i = 0; i < pVStreamData->u32PackCount; i++) {
#if  defined (__DualSys__) || defined (__HuaweiLite__)
        HI_U64 u64PackPhyAddr = pVStreamData->astPack[i].au64PhyAddr[0];
        HI_U32 u32PackLen = pVStreamData->astPack[i].au32Len[0];
        HI_U32 u32PackOffset = pVStreamData->astPack[i].u32Offset;

        HI_U64 s_pDataVirtAddr = (HI_U64)(HI_UL)pVStreamData->astPack[i].apu8Addr[1];
        HI_U64 s_u64DataPhyAddr = pVStreamData->astPack[i].au64PhyAddr[1];
        HI_U32 s_u32DataLen = pVStreamData->astPack[i].au32Len[1];
        HI_VOID *pPackVirtAddr = pVStreamData->astPack[i].apu8Addr[0];

        if (u64PackPhyAddr + u32PackLen >= s_u64DataPhyAddr + s_u32DataLen) {
            /* physical address retrace in offset segment */
            if (u64PackPhyAddr + u32PackOffset >= s_u64DataPhyAddr + s_u32DataLen) {
                HI_VOID *pSrcVirtAddr = (HI_VOID *)(HI_UL)(s_pDataVirtAddr +
                    ((u64PackPhyAddr + u32PackOffset) - (s_u64DataPhyAddr + s_u32DataLen)));
                s32Ret = fwrite(pSrcVirtAddr, u32PackLen - u32PackOffset, 1, pFd);

                if (s32Ret < 0) {
                    printf("fwrite error %d\n", s32Ret);
                }
            } else {
                /* physical address retrace in data segment */
                HI_U32 u32Left = (s_u64DataPhyAddr + s_u32DataLen) - u64PackPhyAddr;
                s32Ret = fwrite((HI_VOID *)((HI_UL)pPackVirtAddr + u32PackOffset), u32Left - u32PackOffset, 1,
                    pFd);

                if (s32Ret < 0) {
                    printf("fwrite error %d\n", s32Ret);
                }

                s32Ret = fwrite((HI_VOID *)(HI_UL)s_pDataVirtAddr, u32PackLen - u32Left, 1, pFd);

                if (s32Ret < 0) {
                    printf("fwrite error %d\n", s32Ret);
                }
            }
        } else {
            /* physical address retrace does not happen */
            s32Ret = fwrite((HI_VOID *)((HI_UL)pPackVirtAddr + u32PackOffset), u32PackLen - u32PackOffset, 1, pFd);

            if (s32Ret < 0) {
                printf("fwrite error %d\n", s32Ret);
            }
        }

#else
        HI_U8* pPackVirtAddr = pVStreamData->astPack[i].apu8Addr[0];
        HI_U32 u32PackLen = pVStreamData->astPack[i].au32Len[0];
        HI_U32 u32PackOffset = pVStreamData->astPack[i].u32Offset;
        s32Ret = fwrite(pPackVirtAddr + u32PackOffset, u32PackLen - u32PackOffset, 1, pFd);

        if (s32Ret < 0) {
            printf("fwrite error %x\n", s32Ret);
        }

#endif

        if (fflush(pFd)) {
            perror("fflush file\n");
        }
    }

    return HI_SUCCESS;
}

HI_S32 SMAPLE_COMM_VENC_DataProc(HI_HANDLE VencHdl, HI_MAPI_VENC_DATA_S *pVStreamData,
    HI_VOID *pPrivateData)
{
    HI_S32 s32Ret = HI_SUCCESS;

    if (pVStreamData->astPack[0].stDataType.enPayloadType == HI_MAPI_PAYLOAD_TYPE_JPEG) {
        s32Ret = SAMPLE_VENC_SaveJpegFile(VencHdl, pVStreamData, pPrivateData);
    } else {
        s32Ret = SAMPLE_VENC_SaveStreamFile(VencHdl, pVStreamData, pPrivateData);
    }

    if (s32Ret != HI_SUCCESS) {
        printf("VENC_SaveFile error s32Ret:%d\n", s32Ret);
    }

    return HI_SUCCESS;
}

HI_S32 SAMPLE_COMM_VENC_LoadOsdBmp(const char *pFileName, BITMAP_S *pstBitmap, HI_BOOL bFil,
    HI_U32 u16FilColor, PIXEL_FORMAT_E enPixelFormat, SAMPLE_COMM_VENC_BUFADDR_S *pstBufAddr)
{
    OSD_SURFACE_S Surface;
    OSD_BITMAPFILEHEADER bmpFileHeader;
    OSD_BITMAPINFO bmpInfo;
    HI_U32 u32BytePerPix = 2;
    HI_U64 u64PhyAddr;
    HI_U32 u32Len;
    HI_VOID*  pVirtAddr;
    HI_S32 s32Ret = HI_SUCCESS;

    if (GetBmpInfo(pFileName, &bmpFileHeader, &bmpInfo) < 0) {
        printf("GetBmpInfo err!\n");
        return HI_FAILURE;
    }

    if (enPixelFormat == PIXEL_FORMAT_ARGB_4444) {
        Surface.enColorFmt = OSD_COLOR_FMT_RGB4444;
    } else if (enPixelFormat == PIXEL_FORMAT_ARGB_1555) {
        Surface.enColorFmt = OSD_COLOR_FMT_RGB1555;
    } else if (enPixelFormat == PIXEL_FORMAT_ARGB_8888) {
        Surface.enColorFmt = OSD_COLOR_FMT_RGB8888;
        u32BytePerPix = 4;
    } else {
        printf("enPixelFormat err %d \n", enPixelFormat);
        return HI_FAILURE;
    }

    u32Len = u32BytePerPix * (bmpInfo.bmiHeader.biWidth) * (bmpInfo.bmiHeader.biHeight);

    s32Ret = HI_MAPI_AllocBuffer(&u64PhyAddr, &pVirtAddr, u32Len, "osd_bmp_buffer");
    if(HI_SUCCESS != s32Ret) {
        printf("HI_MAPI_AllocBuffer fail\n");
     return HI_FAILURE;
    }
    printf("phyAddr:%llu,vir:%p\n",u64PhyAddr,pVirtAddr);

#ifdef __DualSys__
    pstBitmap->pData = (HI_U8*)(HI_UL)u64PhyAddr;
#else
    pstBitmap->pData = (HI_U8*)(HI_UL)pVirtAddr;
#endif
    pstBufAddr->u64PhyAddr = u64PhyAddr;
    pstBufAddr->pVirtAddr = pVirtAddr;
    pstBufAddr->u32Len = u32Len;

    printf("++++++++++bitmapAddr:%p\n",pstBitmap->pData);

    if (NULL == pstBitmap->pData) {
        printf("malloc osd memroy err!\n");
        return HI_FAILURE;
    }

    if (0 != CreateSurfaceByBitMap(pFileName, &Surface, pVirtAddr)) {
        free(pstBitmap->pData);
        pstBitmap->pData = HI_NULL;
        return HI_FAILURE;
    }

    pstBitmap->u32Width      = Surface.u16Width;
    pstBitmap->u32Height     = Surface.u16Height;
    pstBitmap->enPixelFormat = enPixelFormat;

    int i, j;
    HI_U16* pu16Temp;
    pu16Temp = (HI_U16*)pstBitmap->pData;

    if (bFil) {
        for (i = 0; i < pstBitmap->u32Height; i++) {
            for (j = 0; j < pstBitmap->u32Width; j++) {
                if (u16FilColor == *pu16Temp) {
                    *pu16Temp &= 0x7FFF;
                }

                pu16Temp++;
            }
        }
    }

    return HI_SUCCESS;
}

HI_S32 SAMPLE_COMM_VENC_ReleaseOsdBmp(SAMPLE_COMM_VENC_BUFADDR_S *pstBufAddr)
{
    HI_S32 s32Ret = HI_SUCCESS;
    if (HI_NULL == pstBufAddr->pVirtAddr) {
        printf("[%s][%d] stBitmap.pData is NULL\n", __FUNCTION__, __LINE__);
        return HI_FAILURE;
    }

    s32Ret = HI_MAPI_FreeBuffer(pstBufAddr->u64PhyAddr, pstBufAddr->pVirtAddr, pstBufAddr->u32Len);
    if(HI_SUCCESS != s32Ret) {
        printf("HI_MAPI_FreeBuffer fail s32Ret:%x\n", s32Ret);
        return s32Ret;
    }
    return HI_SUCCESS;
}

static HI_S32 VENC_ConfigBitmapOsdAttr(HI_U32 u32DispNum, HI_MAPI_OSD_BIND_MOD_E enBindedMod,
    HI_MAPI_OSD_ATTR_S *pstAttr)
{
    if (pstAttr == NULL) {
        printf("fuc:%s,line:%d, para is null!\n", __FUNCTION__,__LINE__);
        return HI_FAILURE;
    }

    HI_S32 s32Ret = HI_SUCCESS;
    HI_U32 i = 0;
    memset(pstAttr, 0, sizeof(HI_MAPI_OSD_ATTR_S));
    pstAttr->u32DispNum = u32DispNum;
    for (i = 0; i < HI_MAPI_OSD_MAX_DISP_CNT; i++) {
        pstAttr->astDispAttr[i].bShow = HI_TRUE;
        pstAttr->astDispAttr[i].enBindedMod = enBindedMod;
        pstAttr->astDispAttr[i].ModHdl = 0;
        pstAttr->astDispAttr[i].ChnHdl = i;
        pstAttr->astDispAttr[i].u32FgAlpha = 128; // 128 is foreground Alpha
        pstAttr->astDispAttr[i].stStartPos.s32X = 10; // 10 is %10 X axle with resolution ratio
        pstAttr->astDispAttr[i].stStartPos.s32Y = 10; // 10 is %10 Y axle with resolution ratio
        pstAttr->astDispAttr[i].enCoordinate = HI_MAPI_OSD_COORDINATE_RATIO_COOR;
        pstAttr->astDispAttr[i].u32Batch = HI_MAPI_OSD_TYPE_BITMAP;
    }

    pstAttr->stContent.enType = HI_MAPI_OSD_TYPE_BITMAP;
    pstAttr->stContent.u32Color = 0xffff; // 0xffff osd corlor

    return s32Ret;
}

HI_S32 SAMPLE_COMM_VENC_StartSingleOsd(HI_S32 u32DispNum, HI_MAPI_OSD_BIND_MOD_E enBindedMod)
{
    HI_S32 s32Ret;
    SAMPLE_COMM_VENC_BUFADDR_S stBufAddr;
    HI_CHAR aszBitmap[FILE_FULL_PATH_MAX_LEN] = "../../common/resource/mm.bmp";
    HI_S32 s32OsdIdx = 0;
    HI_MAPI_OSD_ATTR_S stAttr;
    SAMPLE_CHECK_RET(VENC_ConfigBitmapOsdAttr(u32DispNum, enBindedMod, &stAttr));

    SAMPLE_CHECK_RET(SAMPLE_COMM_VENC_LoadOsdBmp(aszBitmap, &stAttr.stContent.stBitmapContent,
                                                  HI_FALSE, 0, PIXEL_FORMAT_ARGB_1555, &stBufAddr));
    s32Ret = HI_MAPI_OSD_SetAttr(s32OsdIdx, &stAttr);
    if (s32Ret != HI_SUCCESS) {
        SAMPLE_CHECK_RET(SAMPLE_COMM_VENC_ReleaseOsdBmp(&stBufAddr));
        return s32Ret;
    }
    s32Ret = HI_MAPI_OSD_Start(s32OsdIdx);
    if (s32Ret != HI_SUCCESS) {
        SAMPLE_CHECK_RET(SAMPLE_COMM_VENC_ReleaseOsdBmp(&stBufAddr));
        return s32Ret;
    }

    printf("Press Enter key to stop record...\n");
    HI_CHAR cmd[SAMPLE_CMD_MAX_LEN];
    (void)fgets(cmd, SAMPLE_CMD_MAX_LEN, stdin);

    SAMPLE_CHECK_RET(SAMPLE_COMM_VENC_ReleaseOsdBmp(&stBufAddr));
    SAMPLE_CHECK_RET(HI_MAPI_OSD_Stop(s32OsdIdx));

    return HI_SUCCESS;
}

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* End of #ifdef __cplusplus */
